cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)
project(MMseqs CXX C)
message("-- Source Directory: ${CMAKE_CURRENT_SOURCE_DIR}")
message("-- Project Directory: ${PROJECT_SOURCE_DIR}")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

set(FRAMEWORK_ONLY 0 CACHE BOOL "Framework mode (don't create mmseqs executable)")
set(HAVE_SANITIZER 0 CACHE BOOL "Have sanitizers")
set(INSTALL_UTIL 1 CACHE BOOL "Install utility scripts")
set(VERSION_OVERRIDE "" CACHE STRING "Override version string in help and usage messages")
set(DISABLE_IPS4O 0 CACHE BOOL "Disabling IPS4O sorting library requiring 128-bit compare exchange operations")
set(HAVE_AVX2 0 CACHE BOOL "Have CPU with AVX2")
set(HAVE_SSE4_1 0 CACHE BOOL "Have CPU with SSE4.1")
set(HAVE_SSE2 0 CACHE BOOL "Have CPU with SSE2")
set(HAVE_POWER9 0 CACHE BOOL "Have POWER9 CPU")
set(HAVE_POWER8 0 CACHE BOOL "Have POWER8 CPU")
set(HAVE_ARM8 0 CACHE BOOL "Have ARMv8 CPU")
set(HAVE_S390X 0 CACHE BOOL "Have s390x architecture")
set(NATIVE_ARCH 1 CACHE BOOL "Assume native architecture for SIMD. Use one of the HAVE_* options or set CMAKE_CXX_FLAGS to the appropriate flags if you disable this.")
set(USE_SYSTEM_ZSTD 0 CACHE BOOL "Use zstd provided by system instead of bundled version")

if (HAVE_SANITIZER)
    include(FindUBSan)
    include(FindASan)
    include(FindMSan)
    include(FindTSan)
endif ()

if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif ()

# find compiler
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    message("-- Compiler is clang(++)")
    set(CMAKE_COMPILER_IS_CLANG 1)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
    message("-- Compiler is GNU ")
    if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.9.0")
        message(FATAL_ERROR "Insufficient gcc version")
    endif ()
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel")
    message("-- Compiler is icc(++)")
    set(CMAKE_COMPILER_IS_ICC 1)
endif ()

# see https://wiki.debian.org/ArchitectureSpecificsMemo for char signedness
set(MMSEQS_CXX_FLAGS "-fsigned-char")

# SIMD instruction sets support
set(MMSEQS_ARCH "")
if (HAVE_AVX2)
    if (CMAKE_COMPILER_IS_CLANG)
        set(MMSEQS_ARCH "${MMSEQS_ARCH} -mavx2 -mcx16")
    else ()
        set(MMSEQS_ARCH "${MMSEQS_ARCH} -mavx2 -mcx16 -Wa,-q")
    endif ()
    set(X64 1)
elseif (HAVE_SSE4_1)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -msse4.1 -mcx16")
    set(X64 1)
elseif (HAVE_SSE2)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -msse2")
    set(DISABLE_IPS4O 1)
    set(X64 1)
elseif (HAVE_POWER9)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -mcpu=power9 -mvsx")
    set(PPC64 1)
elseif (HAVE_POWER8)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -mcpu=power8 -mvsx")
    set(PPC64 1)
elseif (HAVE_ARM8)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -march=armv8-a+simd")
    set(ARM 1)
elseif (HAVE_S390X)
    set(MMSEQS_ARCH "${MMSEQS_ARCH} -march=z14 -mzarch -mzvector")
    set(ZARCH 1)
endif ()

if (NATIVE_ARCH AND (MMSEQS_ARCH STREQUAL ""))
    if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*|aarch64.*|AARCH64.*)")
        set(ARM 1)
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "PPC64*|ppc64*|powerpc64*")
        set(PPC64 1)
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|AMD64")
        set(X64 1)
    elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86|X86")
        set(X86 1)
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390")
        set(ZARCH 1)
    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc")
        set(SPARC 1)
    else ()
        message(WARNING "CPU without native SIMD instructions. Performance will be bad.")
    endif ()
    if (EMSCRIPTEN)
        set(MMSEQS_ARCH "-msimd128 -s WASM=1 -s ASSERTIONS=1")
    elseif (X86 OR X64)
        include(CheckSSEFeatures)
        if (NOT HAVE_SSE4_1_EXTENSIONS)
            if (NOT HAVE_SSE2_EXTENSIONS)
                message(FATAL_ERROR "At least SSE2 is needed to compile")
            endif ()
            message(WARNING "At least SSE4.1 is needed for best performance")
        endif ()
        # clang has a problem with march=native on travis
        if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.0.0")
            set(MMSEQS_ARCH "${SSE_FLAGS}")
        else()
            set(MMSEQS_ARCH "-march=native")
        endif()
    elseif (PPC64 OR ARM)
        set(MMSEQS_ARCH "-mcpu=native")
    elseif (ZARCH)
        set(MMSEQS_ARCH "-mcpu=native -mzvector")
    else ()
        set(MMSEQS_ARCH "-march=native")
    endif ()
endif ()

if (NOT (MMSEQS_ARCH STREQUAL ""))
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} ${MMSEQS_ARCH}")
endif ()

if (CYGWIN OR ARM OR PPC64)
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -D_GNU_SOURCE=1")
endif ()

if (CMAKE_COMPILER_IS_ICC)
    # default -fp-model results in inconsistent results in profile search
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -fp-model precise")
endif ()

# Apple specific features
if (APPLE)
    # macOS SDK started using _Atomic (in ucred.h) which g++ does not support
    # __APPLE_API_STRICT_CONFORMANCE makes sysctl.h not include apis like ucred.h
    # See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89864
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -D__APPLE_API_STRICT_CONFORMANCE")
endif ()

if (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -D_WITH_GETLINE")
endif ()

if (CYGWIN)
    # default cygwin allocator (dlmalloc) locks on every allocation and destroys MT performance
    add_subdirectory(lib/nedmalloc)
    # IPS4O seems to deadlock on cygwin
    set(DISABLE_IPS4O 1)
endif()

if ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.0.0")
        OR (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.1.0"))
    # clang before v5 throws compile errors on ips4o
    set(DISABLE_IPS4O 1)
endif ()

if (PPC64 OR SPARC OR ZARCH)
    # FIXME: investigate why on ppc the regression seems to fail randomly
    set(DISABLE_IPS4O 1)
endif ()

set(MMSEQS_C_FLAGS "${MMSEQS_CXX_FLAGS}")
set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -std=c++1y")
# Compiler-specific features
if (CMAKE_COMPILER_IS_CLANG AND (NOT EMSCRIPTEN))
    set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++11")
    set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
    set(MMSEQS_CXX_FLAGS "${MMSEQS_CXX_FLAGS} -stdlib=libc++")
endif ()

if (USE_SYSTEM_ZSTD)
    include(FindPackageHandleStandardArgs)
    find_path(ZSTD_INCLUDE_DIRS NAMES zstd.h REQUIRED)
    # We use ZSTD_findDecompressedSize which is only available with ZSTD_STATIC_LINKING_ONLY
    find_library(ZSTD_LIBRARIES NAMES libzstd.a libzstd_static REQUIRED)
    find_package_handle_standard_args(ZSTD DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
    mark_as_advanced(ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
    include_directories(${ZSTD_INCLUDE_DIRS})
else ()
    # We use ZSTD_findDecompressedSize which is only available with ZSTD_STATIC_LINKING_ONLY
    set(ZSTD_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib/zstd")
    set(CMAKE_INSTALL_LIBDIR bin)
    set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/lib/zstd/build/cmake/CMakeModules")
    option(ZSTD_LEGACY_SUPPORT "LEGACY SUPPORT" OFF)
    option(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON)
    option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF)
    option(ZSTD_MULTITHREAD_SUPPORT "MULTITHREADING SUPPORT" OFF)
    option(ZSTD_BUILD_PROGRAMS "BUILD PROGRAMS" OFF)
    option(ZSTD_BUILD_CONTRIB "BUILD CONTRIB" OFF)
    option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF)
    include_directories(lib/zstd/lib)
    add_subdirectory(lib/zstd/build/cmake/lib EXCLUDE_FROM_ALL)
    set_target_properties(libzstd_static PROPERTIES COMPILE_FLAGS "${MMSEQS_C_FLAGS}" LINK_FLAGS "${MMSEQS_C_FLAGS}")
    set(ZSTD_LIBRARIES libzstd_static)
endif()

# tinyexpr
include_directories(lib/tinyexpr)
add_subdirectory(lib/tinyexpr EXCLUDE_FROM_ALL)

# microtar
include_directories(lib/microtar)
add_subdirectory(lib/microtar)

# simde
include_directories(lib/simde)

include_directories(lib)
include_directories(lib/simd)
include_directories(lib/gzstream)
include_directories(lib/alp)
include_directories(lib/cacode)
include_directories(lib/ksw2)
include_directories(lib/xxhash)
if (NOT DISABLE_IPS4O)
    include_directories(lib/ips4o)
endif ()

add_subdirectory(lib/cacode)
add_subdirectory(lib/alp)
add_subdirectory(lib/ksw2)
add_subdirectory(data)
add_subdirectory(src)
if (NOT FRAMEWORK_ONLY AND INSTALL_UTIL)
    add_subdirectory(util)
endif ()
